MacOS X Developer Preview 4 Release Notes Copyright © 2000 by Apple Computer, Inc. All Rights Reserved.
This release note provides some guidelines for writing a threaded application. For a related discussion, see Thread-safe Classes in Foundation in the release notes for the Foundation framework.
The notes below are split into the following sections:
lockFocusIfCanDraw
(see "NSView," below) and unlockFocus
.[NSApplication
run]
calling [NSApplication
nextEventMatchingMask: ...]
and [NSApplication
sendEvent:]
. While the Application Kit continues
to work if other threads are involved in the event path,
operations can occur out of sequence. For example if two
different threads are responding to key events, the keys
could be received out of order. By letting the main
thread process events, you achieve a more consistent user
experience. The main thread can send a message (DO, Mach
message, or through a NSConditionLock) to get another
thread to do some work.One major reward from multi-threading your application is improved performance on a multi-processor system.
NSGraphicsContext class represents the drawing context provided by the underlying graphics system. Each NSGraphicsContext instance holds its own independent graphics state: coordinate system, clipping, current font, and so on. An instance of the class is automatically created for each NSWindow instance. Additional instances are created for drawing subthreads so that each thread is assigned to its own drawing context.
One thread can create an image, draw it, pass it off to another thread, which can then draw it, and so forth. The underlying image cache is shared among all threads.
You can create, destroy, resize, move, and perform other
operations with NSViews from different threads. The system
ensures that all drawing is deferred while these operations occur
(through the lockFocusIfCanDraw
and unlockFocus
mentioned above). lockFocusIfCanDraw
ensures that lockFocus
and canDraw
methods are called atomically. Subthread
drawing, now, should look like the code fragment below.
if ([aView lockFocusIfCanDraw]) { /* Drawing operations */ [aView unlockFocus]; }
In a multi-threaded application, the main thread is still
responsible for redisplaying dirty views through the same process
as a single-threaded application. The drawRect:
method of every dirty view is called in the main thread. If the
drawing needs to be done on another thread, the drawRect:
method for the view should arrange for the secondary thread to do
the drawing and not do any drawing in drawRect:
.
If a secondary thread of an application wants to cause
portions of the view to be redrawn on the main thread, the normal
mechanisms work (setNeedsDisplay:
, setNeedsDisplayInRect:
,
and setViewsNeedDisplay:
).
Performance Note: The view system's gstates are per-thread.
Using gstates in a multi-threaded application improves drawing
performance in a way similar to a single-threaded application.
Please use the public method (gstate
) to get at the
gstate of the current thread.
It is commonly believed that "the Foundation is thread-safe and the Application Kit is not". However, this statement is a gross generalization and can be misleading. There are varying degrees of thread-safety, and each person has their own definition of "thread-safety". The issue of "re-entrancy" is also often confused with thread-safety. For the purposes of this note, these terms are defined as follows:
Changes to the Objective-C or Java runtimes by threads while other threads are present are outside the scope of this note.
Some objects are thread-safe because they are immutable and thus can't be modified. This doesn't prevent one thread from deallocating the object "out from under" another thread's use.
In general, the collection classes (for example, NSMutableArray, NSMutableDictionary) are not thread-safe when mutations are concerned. That is, if one or more threads are changing the same array, problems will occur. You must lock around spots where reads and writes occur to assure thread-safety.
Also note that where there are immutable and mutable types of the same general type of object (like NSString/NSMutableString), that since the immutable type (like NSString) is used both as the general type and the immutable type that you cannot depend on the immutability of the type providing thread-safety. For example, if the return value of a method is "NSString *", you cannot depend on that object being unchangable and thread-safe, since an instance of NSMutableString (or some other subclass of NSString) could actually be returned. In fact this applies in general: since an instance of any subclass of X could be returned (or passed as a parameter) for a type "X *", and such subclasses may or may not be thread-safe (and/or re-entrant), you cannot depend on the thread-safety of the type "X *" extending to whatever object you may receive satisfying type "X *".
Finally note that re-entrancy is only possible where operations "call out" to other operations in the same object or on different objects. Retaining and releasing objects is one such "call out" which is sometimes overlooked.
These facilities are generally thread-safe:
The following facilities are generally not thread-safe:
* Note that while these classes might be thread-safe or not, it is not safe to change the underlying data or structure of the underlying data while these objects are in use. For example, in the case of an archiver, it is not safe to change the object graph being archived. For an enumerator, it is not safe for any thread to change the enumerated collection.
Some of the non-thread-safe facilities may be made thread-safe in the future.
The following classes are explicitly re-entrant. All other classes may or may not be re-entrant. Note that a complete analysis for re-entrancy has never been done and this list may not be exhaustive.
Some of the non-re-entrant facilities may be made re-entrant in the future.
Subthreads in NSGraphicsContext are not automatically flushed. You need to explicitly call flushGraphics at the end of subthread drawing.
Some CoreGraphics functions are not thread-safe yet, especially font management functions. AppKit provides higher level locking mechanism to prevent this problem so you are protected from unexpected result caused by threading if you stick to AppKit graphics API, NSBezierPath and NSStringDrawing.
You can now create a window on a secondary thread. The
AppKit ensures that the data structures associated with a window
are deallocated on the main thread in order to avoid race
conditions. There is some possibility that window objects
may leak in an application that deals with a lot of windows
concurrently. There is also a theoretical limit on the
number of windows that can be displayed at the same time.
You can now call [NSApplication postEvent:atStart:] from a
secondary thread to post an event to the main thread's event
queue. Order is not guaranteed with respect to user input
events, however. The main thread of the application is
still responsible for handling events in the event queue.
An explicit call to [NSView display] on a secondary thread
will no longer cause recursive display on that thread. The
call to display will be treated as if it were a call to [NSView
setNeedsDisplay:YES], and the recursive display will be done on
the main thread.
You can now create a modal window on a secondary thread. The AppKit blocks the calling secondary thread while the main thread is running the modal loop.